#include "stdafx.h"

#pragma once
#include "GlobalDefinition.h"

// invisible to the user
struct MemoryBlock
{
	unsigned short nSize;  // the size of the usable area within the block
	unsigned short nFree;  // the num of the free unit
	unsigned short nFirst; // the index of the first free unit, begining with 0
	unsigned short nDummyAlign1;
	MemoryBlock* pNext;	   // the pointer to the next block of this pool
	char aData[1]; // the begin of the block usable area

	static void* operator new(size_t, unsigned short nUnitCount, unsigned short nUnitSize)
	{
		return ::operator new (sizeof(MemoryBlock) + nUnitCount * nUnitSize);
	}

	static void operator delete(void *p, size_t)
	{
		::operator delete (p);
	}

	MemoryBlock(unsigned short nUnitCount = 1, unsigned short nUnitSize = 0)
		: nSize(nUnitCount * nUnitSize),
		  nFree(nUnitCount - 1),
		  nFirst(1),
		  pNext(NULL)
	{
		char * pData = aData;

		// please pay attention to the structure 
		//   (index)  (next free index)
		//     0       1
		//     1       2
		//     2       3
		//     3       4
		//     4       5
		//     5       6
		for (unsigned short i = 1; i <= nUnitCount; i++)
		{
			// the first 2 bytes of the unit contains the next free unit following this unit
			// but the first 2 bytes of the last unit is the total unit counts.
			*reinterpret_cast<unsigned short*>(pData) = i;
			pData += nUnitSize;
		}
	}
	
	~MemoryBlock(){}
};

// one MemoryPool can only deal with one unique size
class CArena
{
private:
	MemoryBlock* pBlock;
	unsigned short nUnitSize;
	unsigned short nInitSize;
	unsigned short nGrowSize;

public:
	CArena(unsigned short nUnitSize, 
		unsigned short nInitSize = 1024, 
		unsigned short nGrowSize = 256);
	~CArena();

	// for one MemoryPool can only contain one size data, so it is unnecessary to 
	// give the size. Just Alloc one memory is enough
	void* Alloc();
	void Free( void*p );

public:
	// for test only
	int GetUnitSize(){return nUnitSize; }
	int GetInitSize(){return nInitSize; }
	int GetGrowSize(){return nGrowSize; }

	int GetBlockCount() 
	{
		MemoryBlock* pTemp = pBlock;

		int count = 0;
		while(pTemp != NULL)
		{
			count++;
			pTemp = pTemp->pNext;
		}
		return count;
	}

	// get the next num of the free unit of address p's block
	int FirstFreeUnitIndex(void* p);
	// get the num of the free unit of address p's block
	int GetFreeUnitCount(void* p);

	// please pay attention to the structure 
	//   (index)  (next free index)
	//     0       1
	//     1       2
	//     2       3
	//     3       4
	//     4       5
	//     5       6

	// get the next free unit index
	unsigned short NextFreeUnitIndex(void* p)
	{
		return *((unsigned short*)p);
	}

	// get the index of the p 
	unsigned short GetUnitIndex(void * p)
	{
		return (unsigned short)(((unsigned long)p-(unsigned long)(pBlock->aData)) / nUnitSize);
	}
private:
	int Align(int nSize);
	MemoryBlock* InWhichBlock(void* p, MemoryBlock** pParentBlock = NULL);
};


